package roborally.model;

import be.kuleuven.cs.som.annotate.*;
import roborally.gui.*;

public class Robot implements IRobot{
	/**
	 * 
	 * A class representing a Robot with a position, orientation and energy-level
	 * @invar 	Both x and y positions of the robot are in the range of 0 and the dimension of the Board.
	 * 			|isValidPosition(this.getPosition().getX(),this.getPosition().getY())
	 * @invar	The energy-level of the robot is always positive and smaller than or equal to maxEnergy and maxEnergy has to be positive as well.
	 * 			|isValidEnergy(this.getEnergy(), this.getMaxEnergy())
	 * @invar	The robot always has a valid orientation. This means that its Enum Orientation instance should always be effective.
	 * 			|isValidOrientation(this.getOrientation())
	 * @version 1.0
	 * @author Daan Camps, Jonas Willaeys
	 */
	
	//****************************** Constructors *******************************************************************************
	/**
	 * Initialize a new Robot via this default constructor. This Robot has an energy-level equal to its maximum energy-level of 20000 Ws.
	 * It is situated at the origin and oriented downwards.
	 * 
	 * @effect This new Robot is initialized with a full energy-level of 20000 Ws, placed in the origin and oriented downwards.
	 * 		   |this(0, 0, Orientation.DOWN, 20000, 20000)
	 */
	public Robot() //default constructor
	{
		//assign default values
		this(0, 0, Orientation.DOWN, 20000, 20000);
	}
	
	/**
	 * 
	 * @param 	xCo
	 * 		  	x-coordinate on which to robot should be initialized
	 * @param 	yCo
	 * 		 	y-coordinate on which to robot should be initialized
	 * @param 	orientation
	 * 			the orientation of the newly initialized robot given as an Orientation Enum instance of UP,RIGHT,DOWN or LEFT.
	 * @param 	energy
	 * 			the energy-level of the newly initialized robot
	 * @pre		The energy-level given as parameter should be valid.
	 * 			|isValidEnergy(energy,20000)
	 * @effect 	The new robot is initialized according to the parameters and default maxEnergy of 20000
	 * 			|this(xCo, yCo, orientation, energy, 20000)
	 */
	public Robot(long xCo, long yCo, Orientation orientation, double energy)
	{
		this(xCo, yCo, orientation, energy, 20000);
	}
	
	/**
	 * @param 	xCo
	 * 			x-coordinate on which to robot should be initialized
	 * @param 	yCo
	 * 			y-coordinate on which to robot should be initialized
	 * @param 	orientation
	 * 			the orientation of the newly initialized robot given as an Orientation Enum instance of UP,RIGHT,DOWN or LEFT.
	 * @param 	energy
	 * 			the energy-level of the newly initialized robot
	 * @param	maxEnergy
	 * 			the maximum energy-level of the newly initialized robot
	 * @pre		The energy-level given as parameter should be valid.
	 * 			|isValidEnergy(energy,maxEnergy)
	 * @effect	initialize all instance variables according to parameters
	 * 			|setEnergy(energy), setMaxEnergy(maxEnergy), setPosition(x,y), setOrientation(orientation) 
	 */
	public Robot(long xCo, long yCo, Orientation orientation, double energy, double maxEnergy)
	{
		this.setMaxEnergy(maxEnergy);
		this.setPosition(xCo, yCo);
		this.setOrientation(orientation);
		this.setEnergy(energy);
	}
	//---------------------------------------------------------------------------------------------------------------------------------
	
	//****************************** related to position ******************************************************************************
	/**
	 * 	A Position instance that contains two longs, respectively for x- and y-coordinate.
	 * 	The x-coordinate is then accessed by: position.getX()
	 * 	and for the y-coordinate: position.getY()
	 */
	private Position position = new Position();
	
	/**
	 * @return Returns the position of the Robot as an instance of Position
	 */
	@Basic @Raw
	public Position getPosition()
	{
		return this.position;
	}	
	
	/**
	 * @param	xCo
	 * 			The x-coordinate which is to be set to the robot
	 * @param	yCo
	 * 			The y-coordinate which is to be set to the robot
	 * @post	The new position is (xCo, yCo)
	 * 			| new.position.getX() = xCo
	 * 			| new.position.getY() = yCo	
	 * @throws	IllegalArgumentException
	 * 			An IllegalArgumentException is thrown when the position given in the parameters is invalid.
	 * 			|!isValidPosition(xCo,yCo)
	 * @note 	implemented in a defensive way
	 */
	@Basic @Raw
	public void setPosition(long xCo, long yCo) throws IllegalArgumentException
	{
		if(!isValidPosition(xCo,yCo)) throw new IllegalArgumentException();
		position.setPosition(xCo, yCo);
	}
	
	/**
	 * 
	 * @param	xCo
	 * 			The x-coordinate which is to be set to the robot
	 * @param	yCo
	 * 			The y-coordinate which is to be set to the robot
	 * @return	The method returns true when both position coordinates are positive and smaller than the dimension of the board
	 * 			|result == (xCo >= 0 && yCo >= 0) && ( xCo < Board.dimension && yCo < Board.dimension)
	 */
	public static boolean isValidPosition(long xCo, long yCo)
	{	
		return (xCo >= 0 && yCo >= 0) && ( xCo < Board.dimension && yCo < Board.dimension);
	}
	//---------------------------------------------------------------------------------------------------------------------------------
	
	//****************************** related to orientation ***************************************************************************
	/**
	 *  Orientation instance referring to a UP,RIGHT,DOWN or LEFT Orientation enum instance
	 */
	private Orientation orientation;
	/**
	 * Two final static boolean variables introduced for readability.
	 */
	public static final boolean CLOCKWISE = true;
	public static final boolean COUNTERCLOCKWISE = false;
	
	/**
	 * @return 	Returns the orientation of the robot represented as its according integer value.
	 * 			|result == this.orientation.ordinal()
	 */
	@Basic @Raw
	public int getIntOrientation()
	{
		return this.orientation.ordinal();
	}
	
	/**
	 * @return 	Returns the orientation of the robot as an instance of the Enum Orientation.
	 * 			|result == this.orientation
	 */
	@Basic @Raw
	public Orientation getOrientation()
	{
		return this.orientation;
	}
	
	/**
	 * @param	orientation
	 * 			An instance of the Enum Orientation
	 * @post	The orientation of the robot will be set to the given orientation in case it is a valid one.
	 * 			| if(isValidOrientation(orientation)) then (this.orientation = orientation)
	 * @note 	implemented in a total way
	 */
	@Basic @Raw
	public void setOrientation(Orientation orientation)
	{	
		if(isValidOrientation(orientation)) this.orientation = orientation;
		//if the given orientation is not valid, keep the current orientation of the robot (do nothing).
	}

	/**
	 * Checks if the given orientation is valid.
	 * @param 	orientation
	 * 			An instance of the Enum Orientation.
	 * @return 	If the orientation instance is effective (if it is not a null reference), then it is an instance of the Enum Orientation and thus is valid and we return true. 
	 * 			Else return false.
	 * 			| result == (orientation != null)
	 */
	public static boolean isValidOrientation(Orientation orientation)
	{
		return orientation != null;
	}
	//---------------------------------------------------------------------------------------------------------------------------------
	
	//****************************** related to energy ********************************************************************************
	/**
	 * @return 	The energy-level of this robot.
	 * 			|result == this.energy
	 */
	@Basic @Raw
	public double getEnergy()
	{
		return this.energy;
	}
	
	/**
	 * @return 	The maximum energy-level of this robot.
	 * 			|result == this.maxEnergy
	 */
	@Basic @Raw
	public double getMaxEnergy()
	{
		return this.maxEnergy;
	}
	
	/**
	 * @return 	Returns the ratio of the energy-level of the robot with its maximum energy-level. (as a double)
	 * 			|result == this.getEnergy() / this.getMaxEnergy()
	 */
	@Basic @Raw
	public double getEnergyFraction()
	{
		return this.getEnergy()/this.getMaxEnergy();
	}
	
	/**
	 * @param	energy
	 * 			An energy-level for a robot.
	 * @pre		The energy-level given as parameter should be valid.
	 * 			|isValidEnergy(energy, this.getMaxEnergy())
	 * @post	The new energy-level of the robot will be the one given as parameter.
	 * 			|new.getEnergy() == energy
	 * @note	implemented in a nominal way
	 */
	@Basic @Raw
	public void setEnergy(double energy)
	{
		assert (!isValidEnergy(energy, this.getMaxEnergy()));
		this.energy = energy;
	}
	
	/**
	 * @param	maxEnergy
	 * 			An energy-level for a robot to be used as a maximum level.
	 * @pre		The parameter maxEnergy should be a valid energy-level for the robot.
	 * 			|isValidEnergy(this.getEnergy(), maxEnergy)
	 * @post	The new maximum energy-level of the robot will be the one given as parameter.
	 * 			|new.getMaxEnergy() == maxEnergy
	 * @note	implemented in a nominal way
	 */
	@Basic @Raw
	public void setMaxEnergy(double maxEnergy)
	{
		assert (!isValidEnergy(this.getEnergy(), maxEnergy));
		this.maxEnergy = maxEnergy;
	}
	
	/**
	 * 	A variable that represents the energy-level of this robot.
	 */
	private double energy;	//in Ws
	/**
	 * 	A variable that represents the maximum energy-level of this robot.
	 */
	private double maxEnergy; //in Ws
	
	/**
	 *  ECOST_MOVE is a constant class value that represents the energy that is consumed after each move()
	 *  ECOST_TURN is a constant class value that represents the energy that is consumed after each turn()
	 */
	public static final double ECOST_MOVE = 500;
	public static final double ECOST_TURN = 100;
	
	/**
	 * Recharges the robot with a certain energy amount.
	 * @param energyAmount
	 * @pre		The energy amount given as parameter should be positive and the sum of this amount and the current energy-level of the robot should not be larger than the robot's max energy-level.
	 * 			|energyAmount >= 0 && isValidEnergy(this.getEnergy()+energyAmount,this.getMaxEnergy())
	 * @effect	The new energy-level of the robot is the sum of the old one and the energy amount given as parameter.
	 * 			|setEnergy(this.getEnergy()+energyAmount)
	 */
	public void recharge(double energyAmount)
	{
		assert !(energyAmount >= 0 && isValidEnergy(this.getEnergy()+energyAmount,this.getMaxEnergy()));
		setEnergy(this.getEnergy()+energyAmount);
	}
	
	/**
	 * The energy-level of the robot is always greater or equal to zero and smaller than or equal to maxEnergy. 
	 * This also includes that maxEnergy is greater or equal to zero.
	 * @param energy
	 * 			the energy-level of a robot (in Ws)
	 * @param maxEnergy
	 * 			the maximum energy-level of a robot (in Ws)
	 * @return	Returns true when the energy-level of the robot is greater or equal to zero and smaller than or equal to maxEnergy. If not, false is returned.
	 * 			|result == (energy >= 0) && (energy <= maxEnergy)
	 */
	public static boolean isValidEnergy(double energy, double maxEnergy)
	{
		return (energy >= 0) && (energy <= maxEnergy);
	}
	//---------------------------------------------------------------------------------------------------------------------------------
	
	//****************************** Mutator methods, advanced inspector methods and help methods *************************************
	/**
	 * Turns the robot in clockwise or counterclockwise direction.
	 * @param 	clockwise
	 * 			A boolean indicating if the robot has to turn clockwise (true) or counterclockwise(false)
	 * @pre		When invoking this method the energy-level of the robot must be sufficient to turn. Turning uses ECOST_TURN Ws.
	 * 			|isValidEnergy(this.getEnergy()-ECOST_TURN, this.getMaxEnergy())
	 * @effect	The robot consumes ECOST_TURN Ws in energy
	 * 			|setEnergy(this.getEnergy()-ECOST_TURN)
	 * @effect	If the robot has to turn clockwise, the orientation will be changed in that direction by 90 degrees.
	 * 			|if clockwise then setOrientation( Orientation.values()[this.getIntOrientation()+1)%4] )
	 * @effect	If the robot has to turn counterclockwise, the orientation will be changed in that direction by 90 degrees.	
	 * 			|if !clockwise then( if (this.getIntOrientation() - 1 < 0) then setOrientation(Orientation.LEFT) else setOrientation( Orientation.values()[this.getIntOrientation() - 1)] )
	 */
	public void turn(boolean clockwise)
	{
		assert !isValidEnergy(this.getEnergy()-ECOST_TURN,this.getMaxEnergy()); //everything energy-related is handled nominally
		int newIntOrientation;
		if(clockwise)
		{
			newIntOrientation = (this.getIntOrientation() + 1) % 4;
		}
		else //counterclockwise
		{
			newIntOrientation = this.getIntOrientation() - 1;
			if(newIntOrientation < 0) newIntOrientation = 3;
		}
		this.setOrientation(Orientation.values()[newIntOrientation]);
		setEnergy(this.getEnergy()-ECOST_TURN);
	}
	
	/**
	 * @pre		When invoking this method the energy-level of the robot must be at least ECOST_MOVE.
	 * 			|isValidEnergy(this.getEnergy()-ECOST_MOVE,this.getMaxEnergy())
	 * @effect	The robot moved one field in its current direction.
	 * 			|setPosition( getPosition().getX() + this.orientation.getDx(), getPosition().getY() + this.orientation.getDy() );
	 * @effect	The robot consumes ECOST_MOVE Ws in energy
	 * 			|setEnergy(this.getEnergy()-ECOST_MOVE)
	 */
	public void move() throws IllegalArgumentException
	{
		assert isValidEnergy(this.getEnergy()-ECOST_MOVE,this.getMaxEnergy());
		long newX = getPosition().getX() + this.orientation.getDx();
		long newY = getPosition().getY() + this.orientation.getDy();
		if(!isValidPosition(newX,newY)) throw new IllegalArgumentException();
		
		setPosition(newX, newY);
		setEnergy(this.getEnergy()-ECOST_MOVE);
	}

	/**
	 * Method that calculates the minimal energy required for this robot to move to a certain position given as parameter.
	 * @param 	x
	 * 			The x-coordinate of the position of which we want to count the energy required to reach.
	 * @param 	y
	 * 			The x-coordinate of the position of which we want to count the energy required to reach.
	 * @return	Returns the minimum amount of energy required to reach a certain position. 
	 * 			This equals the consumed energy of a dummyRobot that moved along the most efficient path to this position.
	 * 			| result == energyConsumed
	 * @note	For clarification view the documenting image.
	 */
	public double getEnergyRequiredToReach(long x, long y) throws IllegalArgumentException
	{
		if(!isValidPosition(x,y)) throw new IllegalArgumentException();
		
		/* We don't actually want to move our robot, we just want to calculate how much energy the robot would consume trying to reach that position.
		 * So we make a dummyRobot as a copy of this robot, let him move to that position over the most optimal path, and return the consumed energy by that dummyRobot. */
		Robot dummyRobot = Robot.copyOf(this);
		
		double energyConsumed = 0;
		dummyRobot.setEnergy(this.getMaxEnergy());
		while( !(dummyRobot.getPosition().getX() == x && dummyRobot.getPosition().getY() == y) ) //while the robot has not arrived on the destination
		{
			dummyRobot.moveOneFieldCloserTo(x, y);
			energyConsumed += dummyRobot.getMaxEnergy() - dummyRobot.getEnergy();
			dummyRobot.setEnergy(this.getMaxEnergy());
		}
		//the dummyRobot has moved over the most efficient path with no inefficient turns and has arrived on the destination
		
		//we check how much energy the robot has used and return this value.
		return energyConsumed;	
	}
	
	/**
	 * A static method, given two robots, moves two robots as closely as possible given their energy-levels. When their energy-levels are sufficient they end up next to each other, 
	 * if not they end up as close as possible up to where their energy-level allows.
	 * @param 	robot1
	 * 		  	the first robot
	 * @param 	robot2
	 * 		  	the second robot
	 * @effect	If the robots are not initially next to each other and do not occupy the same position, the robots will move as closely as possible given their energy-levels. 
	 * 			|while(robots are not next to each other and at least one has sufficient energy to move) do (move one field closer to each other)
	 * @effect	If the robots are initially on the same position, one of them will move one field.
	 * 			|robotFirst.move(): if(exception thrown) then robotLast.move(): if(exception thrown) robotFirst.turn(CLOCKWISE) unless robotFirst: if(one of the 4 special cases of positioning and orientation) then turn(COUNTERCLOCKWISE)
	 */
	public static void moveNextTo(Robot robot1, Robot robot2)
	{
		//We let the robot with the most energy move first
		Robot robotFirst, robotLast;
		double dE = robot1.getEnergy() - robot2.getEnergy();
		if(dE < 0)
		{
			robotFirst = robot2;
			robotLast = robot1;
		}
		else
		{
			robotFirst = robot1;
			robotLast = robot2;
		}
		
		//while the robots are not next to each other and at least one of the robots has sufficient energy to move
		while( !Robot.isNextTo(robotFirst,robotLast) && (isValidEnergy(robotFirst.getEnergy()-ECOST_MOVE, robotFirst.getMaxEnergy()) || isValidEnergy(robotLast.getEnergy()-ECOST_MOVE, robotLast.getMaxEnergy())) )
		{	
			//in case the two robots are positioned on the same field
			if( robotFirst.getPosition().isEqualTo(robotLast.getPosition()) ) 
			{
				try{
					robotFirst.move();
				}
				catch (IllegalArgumentException exc)
				{
					//if moving the robot one field into its orientation results in an exception being thrown because the robot lands on a invalid position
					try{
						//we try to move the other robot
						robotLast.move();
					}
					catch (IllegalArgumentException exc1)
					{
						//if this also results in a thrown exception, we try to turn the first robot and move it
						//turning it clockwise will solve it in most cases but a few listed in the if structure in which case we need to turn the robot counterclockwise!
						if( robotFirst.getPosition().isEqualTo(new Position(0,0)) && robotFirst.getOrientation().equals(Orientation.LEFT)
							|| robotFirst.getPosition().isEqualTo(new Position(Board.dimension-1,0)) && robotFirst.getOrientation().equals(Orientation.UP)
							|| robotFirst.getPosition().isEqualTo(new Position(Board.dimension-1,Board.dimension-1)) && robotFirst.getOrientation().equals(Orientation.RIGHT)
							|| robotFirst.getPosition().isEqualTo(new Position(0,Board.dimension-1)) && robotFirst.getOrientation().equals(Orientation.DOWN) )
							robotFirst.turn(COUNTERCLOCKWISE);
						else robotFirst.turn(CLOCKWISE);
						try{
							//now move the robot
							robotFirst.move();
						}
						catch (IllegalArgumentException exc2)
						{
							//although normally the problem will always eventually be solved, if for some obscure reason it was not, throw the exception
							throw exc2;
						}
					}
				}
			}
			else //if they are on different positions
			{
				robotFirst.moveOneFieldCloserTo(robotLast.getPosition().getX(), robotLast.getPosition().getY());
				if(!Robot.isNextTo(robotFirst,robotLast)) //if they are not already next to each other after moving the first robot, we also move the last robot a field closer to the other one. 
					robotLast.moveOneFieldCloserTo(robotFirst.getPosition().getX(), robotFirst.getPosition().getY());
			}
		}
	}
	
	/**
	 * Checks if two robots are next to each other.
	 * @param 	robot1
	 * 		  	the first robot
	 * @param 	robot2
	 * 		  	the second robot
	 * @return 	returns true if the Manhattan-distance between the robots is exactly one in x or y direction 
	 * 			|result == (|dx| == 1 && |dy| == 0) || (|dx| == 0 && |dy| == 1);
	 */
	public static boolean isNextTo(Robot robot1, Robot robot2)
	{
		long dx = Math.abs(robot1.getPosition().getX() - robot2.getPosition().getX());
		long dy = Math.abs(robot1.getPosition().getY() - robot2.getPosition().getY());
		return (dx == 1 && dy == 0) || (dx == 0 && dy == 1);
	}
	
	/**
	 * A method that turns its prime object into a desired orientation (to move towards the target position) if needed and then moves the robot one field closer towards the position if it has sufficient energy to do so.
	 * @param 	x
	 * 			The x-coordinate of the position the robot should move closer to.
	 * @param 	y
	 * 			The y-coordinate of the position the robot should move closer to.
	 * @throws	IllegalArgumentException
	 * 			An IllegalArgumentException is thrown when the position given in the parameters is invalid.
	 * 			|!isValidPosition(x,y)
	 * @effect	Implying the robot has sufficient energy to move and is not already on the position to move towards, the new position of the robot is one field closer to the position given as a parameter. If needed the robot will also be turned in an optimal direction to move.
	 * 			| if(robot has sufficient energy to turn once) then ( turn if needed into desired orientation ); 
	 * 			| if(robot has sufficient energy to turn again && if needed to turn again to reach a desired orientation) then (turn into desired orientation); 
	 * 			| if(robot has sufficient energy to move) then (this.move())
	 * @effect	If the robot is already on the position given as a parameter then return
	 * 			| if( (this.getPosition().getX() - x) == 0 && (this.getPosition().getY() - y) == 0 ) then return
	 */
	public void moveOneFieldCloserTo(long x, long y) throws IllegalArgumentException
	{ 
		if(!isValidPosition(x,y)) throw new IllegalArgumentException(); //position is handled defensively
		
		//dx and dy represent the fields to travel respectively over the X-axis and Y-axis
		long dx = this.getPosition().getX() - x;
		long dy = this.getPosition().getY() - y;
		
		//we first check if the robot has enough energy to even turn once, in case not we cannot help the robot get any further towards its destination. return.
		if(!isValidEnergy(this.getEnergy()-ECOST_TURN, this.getMaxEnergy())) return; //energy is handled nominally, we assure that the precondition is satisfied.
		
		//We will now turn the robot into the closest desired orientation so that it is facing the destination over X- or Y-Axis (depending on the initial orientation of the robot).
		if(dx < 0) //the destination is on the right of the robot (== the robot is on the left of the destination)
		{
			if(dy < 0) //the destination is beneath of the robot
			{
				//CASE: move from LT (Left-Top) to RB (Right-Bottom)
				//Desired orientation: RIGHT or DOWN
				/* We select the current orientation of the robot in the switch statement 
				 * and accordingly turn the robot to the closest desired orientation if it isn't already oriented as desired. */
				switch (this.getOrientation()) 
				{
				case UP: 
					this.turn(CLOCKWISE); //turn clockwise
					break;
				case LEFT: 
					this.turn(COUNTERCLOCKWISE);
					break;
				}
			}
			else if(dy > 0) //the destination is above of the robot
			{	
				//CASE: move from LB to RT
				//Desired orientation: RIGHT or UP
				switch (this.getOrientation()) 
				{
				case DOWN:
					this.turn(COUNTERCLOCKWISE);
					break;
				case LEFT: 
					this.turn(CLOCKWISE);
					break;
				}				
			}
			else //dy is 0, the robot only moves along the X-Axis
			{
				//CASE: the destination is on the absolute right of the robot
				//Desired orientation: RIGHT
				switch (this.getOrientation()) 
				{
				case UP:
					this.turn(CLOCKWISE);
					break;
				case DOWN: 
					this.turn(COUNTERCLOCKWISE);
					break;
				case LEFT:
					this.turn(CLOCKWISE);
					//In these special cases that the robot requires two turns to acquire the desired orientation, we need to check again if it has enough energy for a second turn. If not we cannot bring the robot any further towards its destination. return.
					if(!isValidEnergy(this.getEnergy()-ECOST_MOVE, this.getMaxEnergy())) return; 
					this.turn(CLOCKWISE);
					break;
				}	
			}
		}
		else if(dx > 0) //the destination is on the left of the robot
		{
			if(dy < 0) //the destination is beneath of the robot
			{
				//CASE: move from RT to LB
				//Desired orientation: LEFT or DOWN
				switch (this.getOrientation()) 
				{
				case UP: 
					this.turn(COUNTERCLOCKWISE); //turn clockwise
					break;
				case RIGHT:
					this.turn(CLOCKWISE);
					break;
				}
			}
			else if(dy > 0) //the destination is above of the robot
			{	
				//CASE: move from RB to LT
				//Desired orientation: LEFT or UP
				switch (this.getOrientation()) 
				{
				case RIGHT:
					this.turn(COUNTERCLOCKWISE);
					break;
				case DOWN:
					this.turn(CLOCKWISE);
					break;
				}				
			}
			else //dy is 0, the robot only moves along the X-Axis
			{
				//CASE: The destination is on the absolute left of the robot
				//Desired orientation: LEFT
				switch (this.getOrientation()) 
				{
				case UP: 
					this.turn(COUNTERCLOCKWISE);
					break;
				case RIGHT:
					this.turn(CLOCKWISE);
					if(!isValidEnergy(this.getEnergy()-ECOST_MOVE, this.getMaxEnergy())) return; 
					this.turn(CLOCKWISE);
					break;
				case DOWN:
					this.turn(CLOCKWISE);
					break;
				}	
			}

		}
		else //dx is 0, the robot only moves along the Y-Axis
		{
			if(dy < 0)
			{
				//CASE: The destination is directly beneath of the robot
				//Desired orientation: DOWN
				switch (this.getOrientation()) 
				{
				case UP:
					this.turn(CLOCKWISE);
					if(!isValidEnergy(this.getEnergy()-ECOST_MOVE, this.getMaxEnergy())) return; 
					this.turn(CLOCKWISE);
					break;
				case RIGHT:
					this.turn(CLOCKWISE);
					break;
				case LEFT: 
					this.turn(COUNTERCLOCKWISE);
					break;
				}	
			}
			else if(dy > 0)
			{
				//CASE: The destination is directly above of the robot
				//Desire orientation: UP
				switch (this.getOrientation()) 
				{
				case RIGHT: 
					this.turn(COUNTERCLOCKWISE);
					break;
				case DOWN:
					this.turn(CLOCKWISE);
					if(!isValidEnergy(this.getEnergy()-ECOST_MOVE, this.getMaxEnergy())) return; 
					this.turn(CLOCKWISE);
					break;
				case LEFT:
					this.turn(CLOCKWISE);
					break;
				}	
			}
			else //the position given is the current position of the Robot!
			{
				return; //no need to move, the robot is already on the position.
			}
		}
		//if the method has come to this line, the robot is facing towards the destination over X or Y-Axis, we move the robot one field up under condition it has sufficient energy.
		if(isValidEnergy(this.getEnergy()-ECOST_MOVE, this.getMaxEnergy())) //energy is handled nominally, we assure that the precondition is satisfied. 
			this.move();
	}
	
	/**
	 * A method that returns a copy of a given robot object.
	 * @param 	robot
	 * 			the robot of which the method must return a copy
	 * @return 	a copy of the robot object given as parameter.
	 * 			|result == new Robot(robot.getPosition().getX(), robot.getPosition().getY(), robot.getOrientation(), robot.getEnergy(), robot.getMaxEnergy())
	 */
	public static Robot copyOf(Robot robot)
	{
		return new Robot(robot.getPosition().getX(), robot.getPosition().getY(), robot.getOrientation(), robot.getEnergy(), robot.getMaxEnergy());
	}
	//------------------------------------------------------------------------------------------------------------------------------------------
}
